using System;
using System.Collections;				// for IList, ICollection, IEnumerable interfaces
using System.Diagnostics;				// mostly for Debug.Assert(...)

namespace Axe.Geometry
{
	using T = Plane3;

	/// <summary>
	/// A collection of planes
	/// </summary>
	public class Plane3Collection : IList, ICollection, IEnumerable, ICloneable 
	{
		/// <summary>
		/// members
		/// </summary>
		private T[] m_data;
		private int m_size;
		private static int m_defaultCapacity = 16;

		/// <summary>
		/// constructors
		/// </summary>
		public Plane3Collection() 
		{
			m_data = new T[m_defaultCapacity];
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		public Plane3Collection(Plane3Collection x) 
		{
			Debug.Assert( x != null );
			m_data = new T[x.Count * 2];
			AddRange(x);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		public Plane3Collection(T[] x) 
		{
			Debug.Assert( x != null );
			m_data = new T[x.Length * 2];
			AddRange(x);
		}

		/// <summary>
		/// properties
		/// </summary>
		public T this[int index] 
		{
			get 
			{
				return m_data[index];
			}

			set 
			{
				m_data[index] = value;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		object IList.this[int index] 
		{
			get 
			{
				return m_data[index];
			}

			set 
			{
				m_data[index] = (T)value;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		bool IList.IsFixedSize
		{
			get{ return false;}
		}

		/// <summary>
		/// 
		/// </summary>
		bool IList.IsReadOnly
		{
			get{ return false;}
		}

		/// <summary>
		/// 
		/// </summary>
		public int Count
		{
			get{ return m_size;}
		}

		/// <summary>
		/// 
		/// </summary>
		bool ICollection.IsSynchronized
		{
			get{ return m_data.IsSynchronized;}
		}

		/// <summary>
		/// 
		/// </summary>
		object ICollection.SyncRoot
		{
			get{ return m_data.SyncRoot;}
		}

		/// <summary>
		/// Methods
		/// </summary>
		/// <mParam name="capacity"></mParam>
		private void EnsureCapacity(int capacity)
		{
			Debug.Assert( capacity >= 0 );
			int currentCapacity = m_data != null? m_data.Length: 0;
			if (currentCapacity >= capacity)
				return;

			if (currentCapacity == 0)
				SetCapacity(m_defaultCapacity);
			else
				SetCapacity(currentCapacity * 2);

		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="capacity"></mParam>
		public void SetCapacity(int capacity)
		{
			Debug.Assert( capacity >= 0 );
			int currentCapacity = m_data != null? m_data.Length: 0;
			if (currentCapacity == capacity)
				return;

			if (currentCapacity > capacity)
				throw new ArgumentOutOfRangeException("value",
					"ArgumentOutOfRange_SmallCapacity");

			T[] temp = new T[capacity];

			if (m_data != null)
				System.Array.Copy(m_data, 0, temp, 0, currentCapacity);

			m_data = temp;
		}

		/// <summary>
		/// 
		/// </summary>
		public void Clear()
		{
			m_size = 0;
			m_data = null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="val"></mParam>
		/// <returns></returns>
		int IList.Add(object val)
		{
			return Add((T)val);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		/// <returns></returns>
		public int Add(T x) 
		{
			EnsureCapacity(m_size + 1);
			m_data[m_size] = x;
			return m_size++;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		public void AddRange(T[] x) 
		{
			Debug.Assert( x != null );
			for (int i=0; i < x.Length; ++i)
				Add(x[i]);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		public void AddRange(Plane3Collection x) 
		{
			Debug.Assert( x != null );
			for (int i=0; i < x.Count; ++i)
				Add(x[i]);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		/// <returns></returns>
		bool IList.Contains(object x) 
		{
			return Contains((T)x);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		/// <returns></returns>
		public bool Contains(T x) 
		{
			for(int cnt = 0; cnt < m_size; ++cnt)
			{
				if (m_data[cnt] == x)
					return true;
			}
			return false;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="array"></mParam>
		/// <mParam name="index"></mParam>
		public void CopyTo(T[] array, int index) 
		{
			Debug.Assert( array != null );
			System.Array.Copy(m_data, 0, array, 0, m_size);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		/// <returns></returns>
		int IList.IndexOf(object x) 
		{
			return IndexOf((T)x);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		/// <returns></returns>
		public int IndexOf(T x) 
		{
			for(int cnt = 0; cnt < m_size; ++cnt)
			{
				if (m_data[cnt] == x)
					return cnt;
			}
			return -1;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="index"></mParam>
		/// <mParam name="x"></mParam>
		void IList.Insert(int index, object x) 
		{
			Insert(index, (T)x);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="index"></mParam>
		/// <mParam name="x"></mParam>
		public void Insert(int index, T x) 
		{
			if (index > m_size)
				throw new ArgumentOutOfRangeException("index");
			if (index == m_size)
				this.Add(x);

			T[] tail = new T[m_size-index];
			Array.Copy(m_data, index, tail, 0, m_size-index);
			m_data[index] = x;
			Array.Copy(tail, 0, m_data, index + 1, m_size-index);
			++m_size;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		void IList.Remove(object x) 
		{
			Remove((T)x);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="x"></mParam>
		public void Remove(T x) 
		{
			for(int cnt = 0; cnt < m_size; ++cnt)
			{
				if (m_data[cnt] == x)
				{
					IList il = (IList)this;
					il.RemoveAt(cnt);
					return;
				}
			}
			throw new ArgumentException("element('" + x + "') not found, can not remove", "x" );
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="index"></mParam>
		void IList.RemoveAt(int index)
		{

			if (index < 0 || index >= m_size )
				throw new ArgumentOutOfRangeException("index");

			T[] tail = new T[m_size-index];
			Array.Copy(m_data, index+1, tail, 0, m_size-index-1);
			Array.Copy(tail, 0, m_data, index, m_size-index-1);
			--m_size;
			return;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="array"></mParam>
		/// <mParam name="index"></mParam>
		void ICollection.CopyTo(Array array, int index)
		{
			Array.Copy(m_data, 0, array, index, m_size);
		}


		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		IEnumerator IEnumerable.GetEnumerator() 
		{
			return new Plane3Enumerator(this);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public Plane3Enumerator GetEnumerator() 
		{
			return new Plane3Enumerator(this);
		}


		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		object ICloneable.Clone()
		{
			Plane3Collection c = new Plane3Collection();
			c.m_size = this.m_size;
			c.m_data = (T[])m_data.Clone();
			return c;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="array"></mParam>
		public void	FromArray( Plane3[] array ) 
		{
			if( array != null ) 
			{
				this.Clear();
				foreach( Plane3 o in array ) 
				{
					this.Add( o );
				}
			}
		}
		
		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public Plane3[] ToArray() 
		{
			Plane3[] array = new Plane3[ this.Count ];
			this.CopyTo( array, 0 );
			return array;
		}

		/// <summary>
		/// 
		/// </summary>
		public void Reverse() 
		{
			int count = this.Count;
			for( int i = 0; i < count / 2; i ++ ) 
			{
				Plane3 temp = this[i];
				this[i] = this[count - i - 1];
				this[count - i - 1] = temp;
			}
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public class Plane3Enumerator : IEnumerator 
	{

		private Plane3Collection temp;
		private T curr;
		private int cursor;

		/// <summary>
		/// 
		/// </summary>
		/// <mParam name="col"></mParam>
		public Plane3Enumerator(Plane3Collection col) 
		{
			temp = col;
			cursor = -1;
		}

		/// <summary>
		/// 
		/// </summary>
		public void Reset()
		{
			cursor = -1;
			curr = Plane3.Zero;
		}

		/// <summary>
		/// 
		/// </summary>
		public T Current 
		{
			get 
			{
				return curr;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		object IEnumerator.Current 
		{
			get 
			{
				return Current;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public bool MoveNext() 
		{
			++cursor;
			if (cursor < temp.Count) 
			{
				curr = temp[cursor];
				return true;
			}
			else
				return false;
		}
	}


}
